Skip to content

feat(fastsync): /fastsync/anchors feed + concurrent (SQLite/WAL) bundle store (#115)#124

Merged
LiranCohen merged 1 commit into
mainfrom
feat/fastsync-bundle-store-sqlite
Jun 9, 2026
Merged

feat(fastsync): /fastsync/anchors feed + concurrent (SQLite/WAL) bundle store (#115)#124
LiranCohen merged 1 commit into
mainfrom
feat/fastsync-bundle-store-sqlite

Conversation

@LiranCohen

Copy link
Copy Markdown
Contributor

Summary

Completes Option B of the fast-sync overlay (epic #111): the verifiable anchor-bundle HTTP feed, plus the concurrency fix that unblocks it.

The concurrency fix

serve (reader) and start (writer) are separate processes. #113's bundle store was bbolt — a single-process exclusive lock — so they couldn't both open it. This converts the bundle store to SQLite/WAL (the same property anchors.db already relies on for concurrent start+serve), keeping the exact Put/Get/Range API so #113's indexer wiring and tests are unchanged. Still fail-closed / no reorg rollback (a stale bundle fails the client's merkle check; re-index overwrites).

The feed

  • internal/fastsync/service.goService, the transport-agnostic serving core (shared with Option C fast-sync C: native P2P messages via a vendored btcd wire (getanchors/anchors, getcas/cas) #122). AnchorBundles(from,to) reads the store, clamped to a max height span + max bundle count.
  • GET /fastsync/anchors?from=&to={"anchors":[{height,txIndex,rawTx hex,branch hex[]}]}, CORS-open. The client reconstructs each bundle and verifies its merkle proof against its own PoW headers — the source can only omit, never forge.
  • --serve-fast-sync now both (a) makes start capture bundles and (b) makes serve expose /fastsync/cas + /fastsync/anchors.

To populate bundles in production: run start --serve-fast-sync (captures going forward; a re-index backfills history).

Tests

  • Service range/order/bounds.
  • Two concurrent Store handles (modeling the start writer + serve reader) read+write the same file — a bbolt store would fail the 2nd Open.
  • The /fastsync/anchors endpoint serialized a bundle that a client reconstructs and VERIFIES after the JSON round-trip (+ CORS, bad-range 400, route-absent 404).

go test -race ./... green (28 packages).

Post-Deploy Monitoring & Validation

  • What to watch: with start --serve-fast-sync, bundles.db grows as anchors are indexed; serve --serve-fast-sync answers GET /fastsync/anchors?from=&to= with bundles a client can verify. The two processes share bundles.db via WAL.
  • Failure signal / trigger: empty /fastsync/anchors despite indexed anchors → start wasn't run with --serve-fast-sync (no capture) or history needs a re-index backfill.
  • Window/owner: when a node opts into serving fast-sync.

Part of #111. Closes #115.

🤖 Generated with Claude Code

…le store (#115)

Completes Option B of the fast-sync overlay (epic #111): the verifiable anchor-bundle
HTTP feed, plus the concurrency fix that unblocks it.

- internal/fastsync/store.go: convert the bundle store from bbolt (single-process
  exclusive lock) to SQLite/WAL, so the `start` writer and the `serve` reader can
  open it concurrently — the same property anchors.db relies on. Same Put/Get/Range
  API, so #113's indexer wiring and tests are unchanged. Still fail-closed / no reorg
  rollback (a stale bundle fails the client's merkle check; re-index overwrites).
- internal/fastsync/service.go: Service — the transport-agnostic serving core (shared
  with Option C #122). AnchorBundles(from,to) reads the store, clamped to a max height
  span + max bundle count.
- internal/api: GET /fastsync/anchors?from=&to= → {"anchors":[{height,txIndex,rawTx
  hex,branch hex[]}]}, CORS-open. The client reconstructs each bundle and verifies its
  merkle proof against its OWN PoW headers — the source can only omit, never forge.
- cmd/ion-node: --serve-fast-sync now both (a) makes `start` capture bundles
  (WithBundleWriter over the SQLite store) and (b) makes `serve` expose /fastsync/cas
  + /fastsync/anchors. node.close() closes the bundle store.

To populate bundles in production, run `start --serve-fast-sync` (captures going
forward; a re-index backfills history).

Tests: Service range/order/bounds; two concurrent Store handles (the start/serve
processes) read+write the same file (a bbolt store would fail the 2nd Open); and the
/fastsync/anchors endpoint serialized a bundle that a client reconstructs and VERIFIES
after the JSON round-trip (+ CORS, bad-range 400, route-absent 404). go test -race
./... green.

Co-authored-by: Liran Cohen <liranlasvegas@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LiranCohen LiranCohen merged commit d181ec5 into main Jun 9, 2026
1 check passed
@LiranCohen LiranCohen deleted the feat/fastsync-bundle-store-sqlite branch June 9, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fast-sync 4/7: fast-sync wire protocol (getanchors/anchors, getcas/cas)

1 participant